#include <iostream>
#include <cstring>
#include <chrono>
#include <thread>
#include <cstdlib>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif

struct ICMPHeader {
    uint8_t type;
    uint8_t code;
    uint16_t checksum;
    uint16_t id;
    uint16_t sequence;
};

uint16_t calculateChecksum(void* buffer, int length) {
    uint16_t* data = reinterpret_cast<uint16_t*>(buffer);
    uint32_t sum = 0;
    for (int i = 0; i < length / 2; ++i) {
        sum += data[i];
    }
    if (length % 2 == 1) {
        sum += reinterpret_cast<uint8_t*>(buffer)[length - 1];
    }
    while (sum >> 16) {
        sum = (sum & 0xFFFF) + (sum >> 16);
    }
    return ~sum;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <IP Address>" << std::endl;
        return 1;
    }

    const char* targetIP = argv[1];

#ifdef _WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "WSAStartup failed." << std::endl;
        return 1;
    }
#endif

    SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sock == INVALID_SOCKET) {
        perror("Socket creation failed");
        return 1;
    }

    sockaddr_in targetAddr{};
    targetAddr.sin_family = AF_INET;
    inet_pton(AF_INET, targetIP, &targetAddr.sin_addr);

    ICMPHeader icmpHeader{};
    icmpHeader.type = 8; // ICMP Echo Request
    icmpHeader.code = 0;
    icmpHeader.id = htons(1);
    icmpHeader.sequence = htons(1);

    char packet[64] = { 0 };
    memcpy(packet, &icmpHeader, sizeof(icmpHeader));
    icmpHeader.checksum = calculateChecksum(packet, sizeof(packet));
    memcpy(packet, &icmpHeader, sizeof(icmpHeader));

    if (sendto(sock, packet, sizeof(packet), 0, reinterpret_cast<sockaddr*>(&targetAddr), sizeof(targetAddr)) < 0) {
        perror("Sendto failed");
        return 1;
    }

    char buffer[1024];
    sockaddr_in fromAddr{};
    socklen_t fromLen = sizeof(fromAddr);
    if (recvfrom(sock, buffer, sizeof(buffer), 0, reinterpret_cast<sockaddr*>(&fromAddr), &fromLen) < 0) {
        perror("Recvfrom failed");
        return 1;
    }

#ifdef _WIN32
    wchar_t ipStr[INET_ADDRSTRLEN];
    if (InetNtop(AF_INET, &fromAddr.sin_addr, ipStr, INET_ADDRSTRLEN) == nullptr) {
        std::cerr << "Failed to convert IP address." << std::endl;
        return 1;
    }
    std::wcout << L"Received ICMP reply from " << ipStr << std::endl;
#else
    char ipStr[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, &fromAddr.sin_addr, ipStr, INET_ADDRSTRLEN) == nullptr) {
        perror("inet_ntop failed");
        return 1;
    }
    std::cout << "Received ICMP reply from " << ipStr << std::endl;
#endif

#ifdef _WIN32
    closesocket(sock);
    WSACleanup();
#else
    close(sock);
#endif

    return 0;
}
